#include <stdio.h>
#include <stdlib.h>
#include<mpi.h>
#include<omp.h>
#include<sys/time.h>
#include<math.h>


int main(int argc, char** argv)//aumentar numero de nos para dividir muliplo de 5
{
    int i = 0;
    int cont = 0;
    int digitos = 0;
    int qnt = 0;
    int numeros[10];
    char frase[10];
    char letra = 'a';
    int tnumero = 0;
    int numero = -1;
    int order = 0;
    int rowtest = -1;
    double error = 0.0;
    int itemax = 0;
    int matrizelem = 0;
    double** matriz;
    double* linhamatriz;
    double* matrizb;
    double* xantigo;
    double* xatualizado;
    double solinicial = 1.0;
    double** matrizcopia;
    double*  matrizbcopia;
    int rank = 0;
    double** matrizdono;
    int nronos = 0;
    int tambloco = 0;
    struct timeval tvBegin, tvEnd;
    double tempototal = 0.0;
    int nrototaldeiteracoes = 0;

    MPI_Status stat;
    MPI_Init(&argc,&argv);
    if ( MPI_Comm_rank(MPI_COMM_WORLD, &rank) == MPI_ERR_COMM){
      printf("############agora ferro#############llllllllllllllllllllll");
    }
    if(rank ==0){
      gettimeofday(&tvBegin, NULL);//=====================================comeca a medicao de tempo=======================================================
    }
    MPI_Comm_size(MPI_COMM_WORLD,&nronos);//usei o numero total de nos pois tive problemas pois o numero nao ficava muito natural pois prefiro essa ideia
    FILE* fp = fopen("matriz500.txt","rb");
    if(fp == NULL) printf("isso n deveria estar aoarecendo");
    //passar a abertura para dentro donde tem rank

    frase[0] = '0';

    //falta criterio de convergencia (talvez), criterio de parada e os dados como o tempo por exemplo e testar as outras, seguindo os e-mails e testar no cluster
    order = leintunitario(fp);
    printf("\n\n----------__________----------\n\nordem e rank respectivos: %d %d", order, rank);
    rowtest = leintunitario(fp);
    ledoubleunitario(fp,&error);
    itemax = leintunitario(fp);
    if(rank==0){
    lematrizparamatriz(fp, &matriz, order);
    lelinhaparamatriz(fp, &matrizb, order);//fiz essa linha nao tendo muita certeza
    copiamatriz(matriz, &matrizcopia, order);
    copiavetor(matrizb, &matrizbcopia,order);
    normaliza(&matriz, &matrizb, order);
    }
    if( (rank<=nronos) && ( rank >= 0)){
      //printf("\nO RANKING ATUAL EH =====-------------------=============%d %d",rank, order);
      int order2 = order;
      int desord = order;
      //printf("\n====== __________ESTA NO RANGE__________===== %d %d", order, order2);//talvez o erro esteja na distribui matriz
      printf("\n......................ANTES DE DISTRIBUIR MATRIZ........................");
      distribuimatriz(&matriz,&matrizb, order, order, nronos,rank);//essa foi a primeira vez q vi um erro tao grotesco e obscuro
      printf("\n......................DEPOIS DE DISTRIBUIR MATRIZ........................");

    criax(&xantigo,order);
    printf("\n......................DEPOIS DE CRIAR Xantigo........................");
    criax(&xatualizado,order);
    printf("\n......................DEPOIS DE CRIAR Xatualizado........................");
    iniciavet(xantigo,solinicial,order);
    printf("\n......................DEPOIS DE INICIALIZAR XANTIGO COM NRO = %d........................",nronos);
    MPI_Barrier(MPI_COMM_WORLD);
    tambloco = order/(nronos - 1);//importante pois calcula o numero de nos
    printf("\n......................DEPOIS DO TAMBLOCO........................");
    printf("\n......................1........................");
    printf("\n......................3........................");
    printf("\n......................4........................");
    printf("\n......................5........................");
    printf("\n......................6........................");
    printf("\n......................7........................");
    printf("\n......................8........................");
    printf("\n......................9........................");
    printf("\n......................10........................");
    printf("\n......................11........................");
    printf("\n......................12........................");
    printf("\n......................13........................");
    printf("\n......................ANTES DO DESOCAMENTO........................");
    nrototaldeiteracoes = deslocamentos(matriz,matrizb,xantigo,xatualizado,order,tambloco,rank,itemax,error);
    printf("\n......................DEPOIS DO DESLOCAMENTOS........................");
    }
    printf("\n......................ANTES DO TESTE DA LINHA........................");
    if(rank==0){
      testedalinha(matrizcopia,xatualizado,matrizbcopia,order,rowtest);//pois ele da a linha e n o indice do vetor e as copias sao backups pois as primeiras foram
    }
    printf("\n......................ANTES DO TESTE DA LINHA........................");

    fclose(fp);//modificadas mas tanto faz as duas foram alocadas na ram
    printf("\n......................DEOPIS DO FECHAMENTO DE ARQUIVO........................");
    /*if( rank!= 0){
      printf("\n\n\n ========\n\nprocesso do no escravo fico na seca");//teste pra ver como saia a impressao
      distribuimatriz(&matriz, order, order, nronos,rank);
    }*/
    //MPI_Barrier(MPI_COMM_WORLD);
    gettimeofday(&tvEnd, NULL);//=====================================================termina a medicao de tempo
    if(rank==0){
      timeval_subtract(&tempototal, &tvEnd, &tvBegin);
      printf("\n\n\n\n\n\n\n\n\n=======================================\n");
      printf("O numero total de iteracoes: %d\n",nrototaldeiteracoes);
      printf("\n ______O____O______esse eh tempo desejado: %lf______O____O______\n",tempototal);
      printf("\n========================================\n\n\n\n\n\n\n\n");
    }
    MPI_Finalize();
    return 0;

}
//falta colocar o numero de casas pelo erro
int testedalinha(double** matriz, double* x, double* matrizb,int ordem, int linha){

 int i = 0;
 double total = 0;
 for(i=0; i<ordem; i++){
   total = total + x[i]*(matriz[linha][i]);
   printf(" %lf",matriz[linha][i]);
 }

 printf("\n=====\na linha %d tem valor: %.10lf e o valor real da matriz B eh: %.4lf\n=====\n", linha+1, total,matrizb[linha]);

return 0;

}

int copiamatriz(double** matriz, double*** matrizcopia, int ordem){

  int i = 0;
  int j = 0;
  (*matrizcopia) = (double**)malloc(ordem*sizeof(double*));
  for(i=0; i<ordem; i++){
    (*matrizcopia)[i] = (double*)malloc(ordem*sizeof(double));
  }
  for(i=0; i<ordem; i++){
    for(j=0; j<ordem; j++){
      (*matrizcopia)[i][j] = matriz[i][j];
    }
  }
  return 0;
}

int copiavetor( double* vet, double** vetcopia, int ordem){//aloca e copia para o vetor

  int i = 0;
  (*vetcopia) = (double*)malloc(ordem*sizeof(double));
  for(i=0; i<ordem; i++){
    (*vetcopia)[i] = vet[i];
  }
  return 0;
}

int deslocamentos ( double** matriz, double* matrizb, double* xantigo, double* xatualizado, int ordem, int tambloco, int rank, int itemax, double erro){
//nao usa uma logica muito boa
 int i = 0;
 int j = 0;
 int k = 0;
 int termina = 0;
 int tag = -1;
 double* particao;
 int m = 0;
 int maximo = 0;
 particao = (double*)malloc(tambloco*(sizeof(double)));
 int nronos;
 printf(" ANTES DE MPI COMM WORLD");
 MPI_Comm_size( MPI_COMM_WORLD, &nronos);
 nronos = nronos;//nao eh mais zero
 MPI_Status stat;
 printf(" DEPOIS DOS STATUS E ETC COM ITEMAX = %d %d",itemax,nronos);

 for(k=0; k<itemax; k++){

    //printf(" dentro do for");
    if( rank == 0){
      MPI_Bcast( &termina, 1, MPI_INT, 0, MPI_COMM_WORLD);
      if( !termina ){
        MPI_Bcast( xantigo, ordem, MPI_DOUBLE, 0, MPI_COMM_WORLD);
        for(i=1;i<nronos;i++){
          MPI_Recv(particao,tambloco,MPI_DOUBLE,MPI_ANY_SOURCE,tag = i,MPI_COMM_WORLD,&stat);
          for(j=0;j<tambloco;j++){
            xatualizado[j + tambloco*(tag - 1)] = particao[j];
          }
        }
        termina = interrompe(xantigo,xatualizado,ordem,erro);
        for(i=0;i<ordem;i++){
            xantigo[i] = xatualizado[i];
        }
      }
      else
        k = itemax;
      maximo++;
    }else{
      if((rank>0) && (rank<nronos)){//nao deveria precisar checar denovo pois o range de ranks jah deveria ter sido garantido pelo main

      while(!termina){
      MPI_Bcast( &termina, 1, MPI_INT, 0, MPI_COMM_WORLD);
      if( !termina){
      MPI_Bcast( xantigo, ordem, MPI_DOUBLE, 0, MPI_COMM_WORLD);
      omp_set_num_threads(3);//talvez deva mudar a parte de schedule pois a ideia a cada thread chegar e excutar qualquer iteracao assim q estiver disponivel
      #pragma omp parallel for schedule( dynamic, tambloco/3)
      for( i = 0; i<tambloco; i++){
        xatualizado[i + tambloco*(rank - 1)] = 0;
        for(j=0; j<ordem; j++){
          xatualizado[i + tambloco*(rank - 1)] = xatualizado[i + tambloco*(rank - 1)] + xantigo[j]*matriz[i][j];//chacar a modificao para os tamanho de blocos pois parece q somente o vetor x eh maiior q o tambloco
        }
        xatualizado[i + tambloco*(rank - 1)] = xatualizado[i + tambloco*(rank - 1)] + matrizb[i];//matrizb[ i + tambloco*(rank - 1)]
      }
      for(i=0;i<tambloco;i++){
        particao[i] = xatualizado[i + tambloco*(rank - 1)];
      }
      MPI_Send(particao,tambloco,MPI_DOUBLE,0,tag = rank, MPI_COMM_WORLD);
      }
      else{
        printf("!!!!!!!!!!!!!!!!!ITERACAO CONCLUIDA!!!!!!!!!!!!!!!! %d %d", i,rank);
        k = itemax;
      }
       //m++;
       //printf("\nm vale %d",m);
      }
      }
    }
 }
 return maximo;
}

int interrompe(double* xantigo, double* xatualizado, int ordem, double erro){

  int i = 0;
  double minum = 0.0;//valores altos
  double mindois = 0.0;
  double auxum = 0.0;
  double auxdois = 0.0;
  double erroatual = 32000.0;

  for( i = 0; i<ordem; i++){//norma infinita de x3 - x4 acho que eh a diferenca entre esses e depois aplica o modulo em cima
    auxum = fabs(xatualizado[i] - xantigo[i]);
    if ( auxum > minum){
      minum = auxum;
    }
    auxdois = fabs(xatualizado[i]);
    if ( auxdois > mindois){
      mindois = auxdois;
    }
  }
  erroatual = minum/mindois;
  if( erroatual < erro ){
    printf("\no erro encontrado foi menor que o erro dado no inicial, portanto satisfez o criterio de parada: %.6lf\n",erroatual);
    return 1;
  }else{
    return 0;
  }
}

int iniciavet(double* vet, double numero, int tamanho){

  int i =0;
  for( i=0; i<tamanho; i++){
    vet[i] = numero;
  }

  return 0;
}


int criax(double** x, int tamanho){

 (*x) = (double*)malloc(tamanho*(sizeof(double)));

 return 0;
}

int distribuimatriz(double*** matrizdono, double** matrizb,int ordem, int nrolinhas, int nronos, int rank){//numero de nos contando com o escravo

  int blocospercorridos = 0;
  int tambloco;
  if( nronos != 0){
    tambloco = nrolinhas/(nronos - 1);
  }else{
    tambloco = 0;
  }
  MPI_Status stat;
  int tag,i,j;
  int nos;
  MPI_Comm_size(MPI_COMM_WORLD, &nos);
  //(*matrizdono) = (double**)malloc(tambloco*(sizeof(double*)));
    //for(i=0;i<tambloco;i++){
      //(*matrizdono)[i] = (double*)malloc(ordem*(sizeof(double)));
    //}

  if( rank == 0){//relembrar q as tags estao com +1
  double* particao = (double*)malloc(tambloco*(sizeof(double)));
  MPI_Bcast(&nronos,1,MPI_INT,0,MPI_COMM_WORLD);
  for(i=1;i<nronos;i++){
    MPI_Send( &blocospercorridos, 1, MPI_INT, blocospercorridos+1, tag = nrolinhas+1,MPI_COMM_WORLD);
    for(j=0;j<tambloco;j++){//o tamanho do bloco jah eh o nuemro de nos nao mestre
      MPI_Send( (*matrizdono)[j + tambloco*blocospercorridos], ordem, MPI_DOUBLE, blocospercorridos+1, tag = (j + tambloco*blocospercorridos),MPI_COMM_WORLD);
    }
    printf("\ndentro do for na linha: %d", i);
    blocospercorridos++;
  }
  blocospercorridos = 0;
  for(j=1;j<nronos;j++){
    for(i=0;i<tambloco;i++){
      particao[i] = (*matrizb)[i + tambloco*blocospercorridos];
    }
    blocospercorridos++;
    printf("\nnronos e linhas %d %d",nronos,nrolinhas);
    MPI_Send( particao, tambloco, MPI_DOUBLE, j, tag = nronos*nronos*nrolinhas,MPI_COMM_WORLD);//vai ter de subtrair um tb
  }
  printf("\nfez todas as impressoes sem problemas: %d", i);
  return 0;

  }else{//o trecho inicial aloca
    int mult;
    MPI_Bcast(&nronos,1,MPI_INT,0,MPI_COMM_WORLD);//pota q pariu, q sentido tem essa bosta
    printf("\n==***recebeu normal***==\n");
    if( nronos != 0){
      tambloco = nrolinhas/(nronos - 1);
    }else{
      printf("\n==ISSO N PODE APARECER SOB HOPOTESE ALGUMA==\n");
      tambloco = 0;
    }
    printf("\nesse eh o valor do tamanho do bloco %d",tambloco);
    (*matrizdono) = (double**)malloc(tambloco*(sizeof(double*)));
    for(i=0;i<tambloco;i++){
      (*matrizdono)[i] = (double*)malloc(ordem*(sizeof(double)));
    }
    (*matrizb) = (double*)malloc(tambloco*(sizeof(double)));

    printf("\nantes do mult");
    MPI_Recv(&mult,1,MPI_INT,0,tag=nrolinhas+1,MPI_COMM_WORLD,&stat);
    printf("\n depois do mult");
    //printf("\n !!!!!!!!!!!!!!!!!!!  NAO PODE ESTAE COM VALOR ERRADOOOOOOOOOOOOOOOOOOOOOO %d %d",tambloco,ordem);
    for(i=0; i<tambloco; i++){
      //printf("\n !!!!!!!!!!!!!!!!!!!  NAO PODE ESTAE COM VALOR ERRADOOOOOOOOOOOOOOOOOOOOOO %d %d",tambloco,ordem);
      MPI_Recv((*matrizdono)[i],ordem,MPI_DOUBLE,0,tag=i + mult*tambloco,MPI_COMM_WORLD,&stat);//tag abitraria
    }
    printf("\nnos e linhas %d %d",nos,nrolinhas);
    MPI_Recv((*matrizb),tambloco,MPI_DOUBLE,0,tag= nos*nos*nrolinhas,MPI_COMM_WORLD,&stat);//todas as alocacoes foram voltadas ao tamanho do bloco
    //parece estar recebendo a matriz corretamente
    printf("\ndeveria terminar a funcao");
  }
  return 1;

}

int normaliza(double*** matriz,double** matrizb,int order){

 int i = 0;
 int j = 0;
 double normalizador = 0 ;
 for (i = 0; i < order; i++ ){
   normalizador = (*matriz)[i][i];
   (*matrizb)[i] = ((*matrizb)[i])/normalizador;
   for ( j = 0; j < order; j++){
     (*matriz)[i][j] = (-1)*(((*matriz)[i][j])/normalizador);
     //printf("%.4lf  ", (*matriz)[i][j]);
   }
   (*matriz)[i][i] = 0.0;
   //printf("%.2lf  ", (*matrizb)[i]);
 }
return 0;
}

int lematrizparamatriz(FILE* fp, double*** matriz, int tamanho){//deve ser quadrada pois a funcao usada dentro dessa considera o mesmo tamanho para as linhas

  int i = 0;
  char letra = 'a';
  (*matriz) = (double**)malloc(tamanho*(sizeof(double*)));
  for (i = 0; i<tamanho; i++ ){
    lelinhaparamatriz(fp,&((*matriz)[i]),tamanho);//como eh quadrada nao ha problemas com o nro de coluas ser o mesmo que o de linhas
    fread(&letra,1,1,fp);//corrige o problema de ter um espaco e depois um enter sendo q na minha concepcao isso n deveria acontecer
    //printf("\n=================================");
  }
  return 0;

}

int lelinhaparamatriz( FILE* fp, double** linha, int tamanho){//o vetor linha sera modificado

  int i = 0;
  (*linha) = (double*) malloc( tamanho*(sizeof(double)) );
  for( i=0; i<tamanho; i++){
    (*linha)[i] = leintunitario(fp);
    //printf("%.2lf  ",(*linha)[i]);
  }

  return 0;
}

//estava dando um erro muito esquisito qndo tentava retornar uma variavel double
int ledoubleunitario(FILE* fp,double* valor){//diferentemente da leitura de inteiro esse le ateh encontrar espaco ou enter
    int tnumero = 0;
    double numerodouble = -1.0;
    char frase[10];
    char letra = 'a';
    char* strend;
    do{
      fread(&letra,1,1,fp);
      if( (((letra>47) && (letra<58)) || (letra == 45)) || ( letra == 46)){
        frase[tnumero] = letra;
        tnumero++;
      }
    }while( (letra != 10) &&(letra !=32));//pois tnumero tem o numero de caracteres
    frase[tnumero] = '\0';//forma uma string
    numerodouble = (double)strtod(frase,&strend);
    *valor = numerodouble;
    return 0;
}

int leintunitario(FILE* fp){
    int tnumero = 0;
    int numero =-1;
    char frase[10];
    char letra = 'a';
    do{
      fread(&letra,1,1,fp);
      if( ((letra>47) && (letra<58)) || (letra == 45) || ( letra == 46)){//le com . e - mas o ponto nao deveria estar nesse if
        frase[tnumero] = letra;
        tnumero++;
      }
    }while( (letra != 10) && (letra!=32));//pois tnumero tem o numero de caracteres
    frase[tnumero] = '\0';//forma uma string
    numero = atoi(frase);
    return numero;
}

int timeval_subtract(double* result, struct timeval *t2, struct timeval *t1)
{
    double tempototal = (((double)(t2->tv_usec - t1->tv_usec))/1000000) +  (t2->tv_sec - t1->tv_sec);
    (*result) = tempototal;
    return 0;
}


